#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>

#define NUM 1024
#define SIZE 32
#define SEP " "
#define NAME g_str[0]

enum REDIRECT
{
    NO_REDIRECT,
    INPUT_REDIRECT,
    OUTPUT_REDIRECT,
    APPEND_REDIRECT
};

char g_argv[NUM];//存放从命令行上获取的字符串
char* g_str[SIZE];//存放分离字符串
char my_env[64];//用于放置我们自己导入的环境变量

//全局状态，用于判断是否重定向
int status=NO_REDIRECT;

//判断是否重定向
char* checkRedirect(const char* str)
{
    char* end=(char*)str+strlen(str)-1;
    while(end>=str)
    {
        if(*end=='>')
        {
            if(end-1>=str&&*(end-1)=='>')
            {
                status=APPEND_REDIRECT;
                *(end-1)='\0';
                end++;
                break;
            }

            status=OUTPUT_REDIRECT;
            *end='\0';
            end++;
            break;
        }
        else if(*end=='<')
        {
            status=INPUT_REDIRECT;
            *end='\0';
            end++;
            break;
        }
        else
        {
            end--;
        }
    }
    
    if(end>=str)
    {
        return end;
    }
    else
    {
        return NULL;
    }
}

//模拟简易Shell
int main()
{
    while(1)
    {
        //1.提示符
        printf("[JK@123-Exec]# ");
        fflush(stdout);
        
        //2.读取
        memset(g_argv,0,sizeof(g_argv));
        while(fgets(g_argv,sizeof(g_argv),stdin)==NULL);
        //情理g_argv中读取上来的\n
        g_argv[strlen(g_argv)-1]='\0';

        //检查是否重定向
        char* redirect=checkRedirect(g_argv);

        //3.分离字符串
        memset(g_str,0,sizeof(g_str));
        g_str[0]=strtok(g_argv,SEP);
        int index=1;
        if(strcmp(NAME,"ll")==0)
        {
            g_str[0]=(char*)"ls";
            g_str[index++]=(char*)"-l";
        }
        if(strcmp(NAME,"ls")==0)
        {
            g_str[index++]=(char*)"--color=auto";
        }
        while(g_str[index++]=strtok(NULL,SEP));

        //测试g_str分离
        //for(index=0;g_str[index];index++)
        //{
        //    printf("g_str[%d]:%s\n",index,g_str[index]);
        //}
       
        //4.内置命令
        //内置命令，让父进程(shell)自己执行的命令，我们叫做内置命令、内建命令
        //内建命令本质其实是shell中的一个函数调用 chdir()
        
        if(strcmp(NAME,"cd")==0)
        {
            //这里让父进程自己去执行
            chdir(g_str[1]);
            continue;
        }

        //导入环境变量
        //命令行输入export JK=111
        if(strcmp(g_str[0],"export")==0&&g_str[1]!=NULL)
        {
            //所谓的导入环境变量，是将该字符串的地址写入到char* env[]这个指针数组中
            //而每一次循环都会使得清空g_str中的数据，从而使得环境变量被清空，所以用my_env保存环境变量
            strcpy(my_env,g_str[1]);
            // printf("strcpy_env:%s\n",my_env);
            int ret=putenv(my_env);
            // if(ret==0)
            // {
            //     printf("export sucess:%s\n",getenv("JK"));
            // }
            continue;
        }
        
        //5.fork
        pid_t id=fork();
        if(id==0)
        {
            //child

            //判断是否重定向
            if(redirect!=NULL)
            {
                int fd=-1;
                switch(status)
                {
                    case 1:
                        fd=open(redirect,O_RDONLY);
                        dup2(fd,0);
                        break;
                    case 2:
                        fd=open(redirect,O_WRONLY|O_CREAT|O_TRUNC,0666);
                        dup2(fd,1);
                        break;
                    case 3:
                        fd=open(redirect,O_WRONLY|O_APPEND|O_CREAT,0666);
                        dup2(fd,1);
                        break;
                    default :
                        break;
                }
            }

            //exec
            // printf("my_env1:%s\n",getenv("JK"));
            // printf("my_env2:%s\n",my_env);
            execvp(NAME,g_str);
            exit(1);
        }
        else
        {
            //father
            int status=0;
            pid_t ret=waitpid(id,&status,0);
            if(ret>0)
            {
                //等待成功，子进程退出
                printf("wait sucess,status code:%d\n",WEXITSTATUS(status));
            }
        }

    }
    return 0;
}
